# pragma once

# include "cpp_tools_Headers.h"
# include "MultiThreadDataManager.h"
# include "ThreadPool.h"

namespace cpp_tools
{
    class gameController
    {
    public:
        struct control_output
        {
            Eigen::VectorXd axes;
            Eigen::VectorXd buttons;

            control_output()
            {
                axes.resize(8);
                buttons.resize(11);
            };

            void setZero()
            {
                axes.setZero(8);
                buttons.setZero(11);
            };
        };

        struct control_output2
        {
            double LT = 0; // 左扳机，行程 0 1
            double RT = 0; // 右扳机，行程 0 1

            double LS_fr = 0; // 左摇杆前后，行程 1(前) -1(后)
            double LS_lr = 0; // 左摇杆左右，行程 1(左) -1(右)
            double RS_fr = 0; // 右摇杆前后，行程 1(前) -1(后)
            double RS_lr = 0; // 右摇杆左右，行程 1(左) -1(右)

            bool L3 = 0; // 左摇杆按下
            bool R3 = 0; // 右摇杆按下

            bool A = 0; // 按键 A
            bool B = 0; // 按键 B
            bool X = 0; // 按键 X
            bool Y = 0; // 按键 Y

            double D_pad_fr = 0; // 十字键(方向键)前后，行程 1(前) -1(后)
            double D_pad_lr = 0; // 十字键(方向键)左右，行程 1(左) -1(右)

            bool LB = 0; // 按键 左肩键
            bool RB = 0; // 按键 右肩键

            bool START = 0; // 按键 START
            bool SELECT = 0; // 按键 SELECT
            bool HOME = 0; // 按键 HOME
        };
        
    private:
        int fd;
        js_event m_event;
        const std::string m_device;

        const double overTime; // 超过这个时间没有收到手柄信号认为超时
        double lastTime = -1;

        const double base = 32767;

        threadPool p_thread;
        mtDataManager<control_output> m_output;

        bool isRuning = true;
        bool isOpen = false;
        bool isOverTime = false;

        bool exitInfo = 0;

    public:
        gameController(const std::string device, const double overtime = 1.0)
            :overTime(overtime), m_device(device)
        {
            fd = open(m_device.data(), O_RDONLY);
            if (fd < 0)
            {
                isOpen = false;

                std::cerr << "无法打开手柄：" << m_device << std::endl;
            }
            else
            {
                isOpen = true;

                std::cout << "开始读取手柄输入..." << std::endl;

                m_output.write().access()->setZero();
            }

            std::thread t_loop([&]() -> void{
                while (isRuning)
                {
                    if (read(fd, &m_event, sizeof(m_event)) == sizeof(m_event))
                    {
                        // std::cout << "时间戳: " << m_event.time << std::endl;
                        lastTime = -1;

                        if (m_event.type & JS_EVENT_BUTTON)
                        {
                            // std::cout << " 按钮事件，按钮编号: " << (int)m_event.number 
                            // << " 状态: " << m_event.value << std::endl;
  
                            if ((int)m_event.number <= 10)
                            {
                                auto ac = m_output.write();
                                ac.access()->buttons((int)m_event.number) = m_event.value;
                            }
                        }
                        else if (m_event.type & JS_EVENT_AXIS)
                        {
                            // std::cout << " 摇杆事件，按钮编号: " << (int)m_event.number 
                            // << " 状态: " << m_event.value << std::endl;

                            if ((int)m_event.number <= 7)
                            {
                                auto ac = m_output.write();
                                ac.access()->axes((int)m_event.number) = m_event.value;
                            }
                        }
                    }
                    else
                    {
                        if (lastTime == -1)
                        {
                            lastTime = getCurrentTimeAsDouble();
                        }
                        
                        double duringTime = getCurrentTimeAsDouble() - lastTime;
                        if (duringTime > overTime && lastTime > 0 && isOpen)
                        {
                            std::cout << "手柄连接超时" << std::endl;
    
                            isOverTime = true;
                        }
                    }

                    if (isOpen == false || isOverTime == true)
                    {
                        fd = open(m_device.data(), O_RDONLY);
                        if (fd < 0)
                        {
                            if (isOverTime)
                            {
                                isOpen = false;
                                
                                std::cerr << "无法打开手柄：" << m_device << std::endl;

                                isOverTime = false;
                            }
                        }
                        else
                        {
                            std::cout << "重新读取手柄输入..." << std::endl;
                            
                            isOpen = true;

                            m_output.write().access()->setZero();
                            
                            isOverTime = false;
                            lastTime = -1;
                        }
                    }
                    
                    std::this_thread::sleep_for(std::chrono::microseconds(100));
                }
            });
            p_thread.push_back(t_loop);
        };

        ~gameController()
        {
            shutdown();

            std::cout << "game controller exit." << std::endl;
        };

        void shutdown()
        {
            isRuning = false;

            if (isDeviceOK() && exitInfo == 0)
            {
                std::cout << "按下手柄任意按键退出..." << std::endl;
                
                exitInfo = 1;
            }

            p_thread.joinAll();

            close(fd);
        };

        /// @brief 获取原始数据
        /// @return 
        control_output get()
        {
            return *m_output.read().access();
        };

        /// @brief 获取定义好的手柄数据
        /// @return 
        control_output2 get2()
        {
            control_output out = *m_output.read().access();
            control_output2 out2;

            out2.A = out.buttons(0);
            out2.B = out.buttons(1);
            out2.X = out.buttons(2);
            out2.Y = out.buttons(3);
            
            out2.LB = out.buttons(4);
            out2.RB = out.buttons(5);

            out2.SELECT = out.buttons(6);
            out2.START = out.buttons(7);
            out2.HOME = out.buttons(8);

            out2.L3 = out.buttons(9);
            out2.R3 = out.buttons(10);

            out2.LS_lr = -out.axes(0)/base;
            out2.LS_fr = -out.axes(1)/base;
            out2.LT = (out.axes(2) + base)/(2*base);
            
            out2.RS_lr = -out.axes(3)/base;
            out2.RS_fr = -out.axes(4)/base;
            out2.RT = (out.axes(5) + base)/(2*base);

            out2.D_pad_lr = -out.axes(6)/base;
            out2.D_pad_fr = -out.axes(7)/base;

            return out2;
        };

        /// @brief 无法打开设备或者连接超时后设备就不OK了
        /// @return 
        bool isDeviceOK()
        {
            return isOpen && !isOverTime;
        };

        /// @brief 输出当前的手柄数据
        void disp()
        {
            auto out = get2();

            std::cout << "\nCONTOLLER INPUT: " << std::endl;

            std::cout << "\tA: " << out.A;
            std::cout << "\tB: " << out.B;
            std::cout << "\tX: " << out.X;
            std::cout << "\tY: " << out.Y << std::endl;

            std::cout << "\tLB: " << out.LB;
            std::cout << "\tRB: " << out.RB << std::endl;

            std::cout << "\tSELECT: " << out.SELECT;
            std::cout << "\tSTART: " << out.START;
            std::cout << "\tHOME: " << out.HOME << std::endl;

            std::cout << "\tL3: " << out.L3;
            std::cout << "\tR3: " << out.R3 << std::endl;

            std::cout << "\tLS_lr: " << out.LS_lr << std::endl;
            std::cout << "\tLS_fr: " << out.LS_fr << std::endl;
            std::cout << "\tRS_lr: " << out.RS_lr << std::endl;
            std::cout << "\tRS_fr: " << out.RS_fr << std::endl;

            std::cout << "\tLT: " << out.LT << std::endl;
            std::cout << "\tRT: " << out.RT << std::endl;

            std::cout << "\tD_paid_lr: " << out.D_pad_lr;
            std::cout << "\tD_paid_fr: " << out.D_pad_fr << std::endl;
        };
    };
} // namespace cpp_tools
